﻿#region Using

using System;
using System.IO;
using System.Windows.Forms;

using DotNetMatrix;

using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;

#endregion

namespace EigenFaces {
	public partial class MainForm : Form {
		private Capture _capture;
		private Classifier _classifier = new Classifier();
		private TraitHandler _traits = new TraitHandler();

		private string _basePath;
		private string _filePath;

		private Matrix _currentTraits;

		public MainForm() {
			InitializeComponent();
			ToggleOptions(null, null);
			Application.Idle += ProcessFrames;
			touchControls1.ClassNameAccepted += OnClassNameAccepted;

			cReduceStrategy.Items.Add(new ClassReduceStrategyFurthestFromCurrent());
			cReduceStrategy.Items.Add(new ClassReduceStrategyFurthestFromCenter());
			cReduceStrategy.Items.Add(new ClassReduceStrategyOldestFirst());
			cReduceStrategy.SelectedItem = _classifier.ReduceStrategy;

			_basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "GesichtserkennungRLPW");
			_filePath = Path.Combine(_basePath, "data.bin");

			LoadHaarFiles();

			if( File.Exists(_filePath) ) {
				ReadData();
			} else {
				ResetData();
			}

			_capture = new Capture();
			_capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, 800);
			_capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, 600);
		}

		private void LoadHaarFiles() {
			var facefile = Path.GetTempFileName();
			WriteTextToFile(Properties.Resources.face_xml, facefile);
			FaceRecognizer.FaceCascade = new HaarCascade(facefile);
			File.Delete(facefile);

			var eyesfile = Path.GetTempFileName();
			WriteTextToFile(Properties.Resources.eyes_xml, eyesfile);
			FaceRecognizer.EyesCascade = new HaarCascade(eyesfile);
			File.Delete(eyesfile);
		}

		private void WriteTextToFile(string resource, string filename) {
			using( var filestream = new FileStream(filename, FileMode.Create) ) {
				using( var writer = new StreamWriter(filestream) ) {
					writer.Write(resource);
				}
			}
		}

		private void ReadData() {
			using( var filestream = new FileStream(_filePath, FileMode.Open) ) {
				using( var reader = new BinaryReader(filestream) ) {
					var dummy = reader.ReadString();
					_traits.ReadFrom(reader);
					cReduceStrategy.SelectedIndex = reader.ReadInt32();
					_classifier.ReadFrom(reader);
					_classifier.ReduceStrategy = cReduceStrategy.SelectedItem as IClassReduceStrategy;
				}
			}
		}

		private void WriteData() {
			using( var filestream = new FileStream(_filePath, FileMode.Create) ) {
				using( var writer = new BinaryWriter(filestream) ) {
					writer.Write("RLPW-GED");
					_traits.WriteTo(writer);
					cReduceStrategy.SelectedItem = _classifier.ReduceStrategy;
					writer.Write(cReduceStrategy.SelectedIndex);
					_classifier.WriteTo(writer);
				}
			}
		}

		private void ResetData() {
			if( folderBrowserDialog1.ShowDialog() == DialogResult.OK ) {
				_classifier.Clear();
				_traits.MakeEigenFacesFromPath(folderBrowserDialog1.SelectedPath);
			}
		}

		private void OnClassNameAccepted(object sender, ClassNameEventArgs args) {
			_classifier.LearnTraits(_currentTraits, args.ClassName);
		}

		private void ProcessFrames(object sender, EventArgs arg) {
			if( _capture == null ) {
				return;
			}

			var frame = _capture.QueryFrame();

			DoScan(frame);

			imageBox1.Image = frame.Flip(FLIP.HORIZONTAL);
		}

		private void DoScan(Image<Bgr, byte> frame) {
			if( !touchControls1.DoScan ) {
				return;
			}

			var faces = new FaceRecognizer(frame);
			if( faces.NumberOfFaces == 1 ) {
				touchControls1.SetWorkingState();
				_currentTraits = _traits.GetTraits(faces.getFaceImage(0));
				string className = _classifier.Classify(_currentTraits);
				touchControls1.SetNameList(_classifier.InnerMap);
				touchControls1.ScanFinished(className);
			}
		}

		private void OnFormClosing(object sender, FormClosingEventArgs e) {
			if( !Directory.Exists(_basePath) ) {
				Directory.CreateDirectory(_basePath);
			}
			WriteData();
		}

		private void ValidateAsInt(object sender, System.ComponentModel.CancelEventArgs e) {
			if( sender is TextBox ) {
				var tb = sender as TextBox;
				int i;
				if( !int.TryParse(tb.Text, out i) ) {
					e.Cancel = true;
				}
			}
		}

		private void ValidateAsDouble(object sender, System.ComponentModel.CancelEventArgs e) {
			if( sender is TextBox ) {
				var tb = sender as TextBox;
				double d;
				if( !double.TryParse(tb.Text, out d) ) {
					e.Cancel = true;
				}
			}
		}

		private void ToggleOptions(object sender, EventArgs e) {
			if( panel1.Width > 30 ) {
				panel3.Visible = false;
				panel1.Width = 30;
			} else {
				panel1.Width = 200;

				tClassSize.Text = _classifier.MaxClassSize.ToString();
				tMinThres.Text = _classifier.MinThreshold.ToString();
				tMaxThres.Text = _classifier.MaxThreshold.ToString();
				cReduceStrategy.SelectedItem = _classifier.ReduceStrategy;

				panel3.Visible = true;
			}
		}

		private void SetValues(object sender, EventArgs e) {
			_classifier.MaxClassSize = int.Parse(tClassSize.Text);
			_classifier.MinThreshold = double.Parse(tMinThres.Text);
			_classifier.MaxThreshold = double.Parse(tMaxThres.Text);
		}

		private void SetReduceStrategy(object sender, EventArgs e) {
			_classifier.ReduceStrategy = cReduceStrategy.SelectedItem as IClassReduceStrategy;
		}

		private void OnResetClick(object sender, EventArgs e) {
			ResetData();
		}

		private const string ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

		private static string GenerateName(Random random, int length) {
			var chars = new char[length];
			for( var i = 0; i < length; i++ ) {
				chars[i] = ValidChars[random.Next(ValidChars.Length)];
			}
			return new string(chars);
		}
	}
}
